/**
 * js一切皆对象, 对象有很多内部方法, 通常是[[xx]]形式
 *  例如, 当我们读取属性的时候, 调用的就是[[Get]]方法
 * 调用函数的时候, 使用的是内部的[[Call]]方法
 * 如果要删除被代理对象的属性需要使用Reflect.deleteProperty
 */
const data = {
  foo: '123',
  get bar() {
    return this.foo + '!'
  }
}
const obj = new Proxy(data, {
  deleteProperty(target, key) {
    console.log('删了')
    Reflect.deleteProperty(target, key)
  }
})

delete obj.foo

console.log(obj.foo, 'obj.foo')

// 此时所有代码为:
let activeEffect
const bucket = new WeakMap()
let effectStack = []

// 收集依赖
function track(target, key) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  if (activeEffect) {
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }
}

// 触发事件
function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  const effectsToRun = new Set()
  effects.forEach((effect) => {
    if (effect !== activeEffect) {
      effectsToRun.add(effect)
    }
  })

  effectsToRun.forEach((effect) => {
    if (effect.options.schedule) {
      effect.options.schedule(effect)
    } else {
      effect()
    }
  })
}

// 代理
const proxyObj = new Proxy(data, {
  get(target, key, receiver) {
    track(target, key)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver) {
    // target[key] = value
    Reflect.set(target, key, value, receiver)
    trigger(target, key)
  }
})

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }
  effectFn.deps = []
  effectFn.options = options
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}

effect(() => {
  console.log(proxyObj.bar, 'foo的值')
})

proxyObj.foo = '1'
